 <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Hello World!</title>
</head>
<body>
<#include "header.ftl" >
<p>


-- substring 取子串<br>
<#--
    这个内建函数从 FreeMarker 2.3.7 开始可用。
    概 要 ： exp?substring(from, toExclusive) ， 也 可 以 作 为
    exp?substring(from)调用
    一个字符串的子串。from 是第一个字符开始的索引。它必须是一个数字而且至少是 0，
    而且要小于或等于 toExclusive，否则错误就会中断模板的处理。 toExclusive 是
    子串中最后一个字符之后的位置索引，换句话说，它比最后一个字符的索引大 1。它必须是
    数字，至少是 0，要小于或等于字符串的长度，否则错误就会中止模板的处理。如果
    toExclusive 被忽略了，那么它默认就是字符串的长度。如果参数不是整型的数字，那
    么数值中只有整型的部分会被使用。
-->
- ${'abc'?substring(0)}
- ${'abc'?substring(1)}
- ${'abc'?substring(2)}
- ${'abc'?substring(3)}<br>
- ${'abc'?substring(0, 0)}
- ${'abc'?substring(0, 1)}
- ${'abc'?substring(0, 2)}
- ${'abc'?substring(0, 3)}<br>
- ${'abc'?substring(0, 1)}
- ${'abc'?substring(1, 2)}
- ${'abc'?substring(2, 3)}


<#--
- ab
- bc
- c
- - -
a
- ab
- ab
- a
- b
- c
-->


<br>--cap_first 首字母大写<br>
${" green mouse"?cap_first},
${"GreEN mouse"?cap_first},
${"- green mouse"?cap_first} <#-- 在"- green mouse"的情形下，第一个单词是-。-->
<#--
Green mouse
GreEN mouse
- green mouse
-->

    <br>---uncap_first 首字母小写<br>


    <br>---capitalize:字符串的所有单词都大写,其他小写<br>
${" green mouse"?capitalize},
${"GreEN mouse"?capitalize}
<#--
    Green Mouse
    Green Mouse
-->

    <br>---chop_linebreak 切断换行符<br>

    <br>---date， time， datetime 日期，时间，时间日期,将会打印出（基于当地（语言）和其他设置决定输出）如下内容：
<#--  date_format， time_format 和 datetime_format<br>
<#assign test1 = "10/25/1995"?date("MM/dd/yyyy")>
<#assign test2 = "15:05:30"?time("HH:mm:ss")>
<#assign test3 = "1995-10-25 03:05 PM"?datetime("yyyy-MM-ddhh:mm a")>
${test1}
${test2}
${test3}

  Oct 25, 1995
  3:05:30 PM
  Oct 25, 1995 3:05:00 PM
-->

    <br>---ends_with 以…结尾<br>
    <#if "redhead"?ends_with("head")>
        "redhead"?ends_with("head") -  true,

    </#if>
    <#if "head"?ends_with("head")>
        "head"?ends_with("head") - true
    </#if>

    <br>--html HTML 格式的转义文本<br>
<#--
 <用&lt 替换；
 >用&gt 替换；
 &用&amp 替换；
 "用&quot 替换；
注意如果你想安全地插入一个属性，你必须在 HTML 模板中使用引号标记（是"，而不
是'）为属性值加引号：
    <input type=text name=user value="${user?html}">
-->

&lt,&gt,&amp,&quot

    <br>--- group 分组<br>

    <br>---index_of 索引所在位置<br>
${"abcabc"?index_of("bc")}  --1
${"abcabc"?index_of("bc", 2)} --4

-- j_string Java 语言规则的字符串转义<br>
<#--
根据 Java 语言字符串转义规则来转义字符串， 所以它很安全的将值插入到字符串类型
中。 要注意它不会在被插入的值的两侧添加引号；你需要在这里使用字符串值。
所有 UCS 编码下指向 0x20 的字符会被转义。当它们在 Java 语言中（比如\n， \t 等）
没有专门的转义序列时， 将会被用 UNICODE 进行转义替换（ \uXXXX）
-->
<#assign beanName = 'The "foo" bean.'>
String BEAN_NAME = "${beanName?j_string}";
<#--
String BEAN_NAME = "The \"foo\" bean.";
-->

    <br>---js_string JavaScript 语言规则的字符串转义<br>
<#--
根据 JavaScript 语言字符串转义规则来转义字符串，所以它很安全的将值插入到字符串
类型中。 要注意，它不会在被插入的值两侧添加引号；而是在字符串中间使用。
引号（ "）和单引号（ '）要被转义。从 FreeMarker 2.3.1 开始，也要将>转义为\>（为
了避免</script>）。
所有在 UCS 编码下指向 0x20 的字符将会被转义。当它们在 JavaScript 中没有专用的转
义序列时（比如\n， \t 等），它们会被 UNICODE 字符代替（ \uXXXX）
-->

<#assign user = "Big Joe's \"right hand\"">
<#--<script>-->
    <#--alert("Welcome ${user?js_string}!");-->
<#--</script>-->

<#--
    <script>
    alert("Welcome Big Joe\'s \"right hand\"!");
    </scrip>
-->

    <br>--json_string JSON 规则的字符串转义<br>
<#--
根据 JSON 语言的字符串规则来转义字符串，所以在字符串中插入值是安全的。要注意
不会在被插入的值两侧添加引号；而是在字符串中间使用。
这不会转义'字符，因为 JSON 字符串必须使用"来括起来。它会在<之后直接出现的/
斜杠）字符转义为\/，来避免</script>等。它也会在]]之后转义>字符为\u003E，
避免退出 XML 的 CDATA 段。
所有在 UCS 编码下指向 0x20 的字符会被转义。当在 JSON 中没有专用的转义序列时（比
\n， \t 等），它们会被 UNICODE 字符代替（ \uXXXX）。
-->


    <br>---last_index_of 最后的索引所在位置<br>
${"abcabc"?last_index_of("ab")}  --3
${"abcabc"?last_index_of("ab", 2)} --0

    <br>--length 字符串长度<br>
${"aaaa"?length}  --4

    <br>---lower_case 小写形式<br>

    <br>---left_pad 距左边<br>
    <#--
    如果它仅仅用 1 个参数，那么它将在字符串的开始插入空白，直到整个串的长度达到参
数指定的值。如果字符串的长度达到指定数值或者比指定的长度还长，那么就什么都不做了。
    -->
    [${""?left_pad(5)}]
    [${"a"?left_pad(5)}]
    [${"ab"?left_pad(5)}]
    [${"abc"?left_pad(5)}]
    [${"abcd"?left_pad(5)}]
    [${"abcde"?left_pad(5)}]
    [${"abcdef"?left_pad(5)}]
    [${"abcdefg"?left_pad(5)}]
    [${"abcdefgh"?left_pad(5)}]
  <#--
    [ ]
    [ a]
    [ ab]
    [ abc]
    [ abcd]
    [abcde]
    [abcdef]
    [abcdefg]
    [abcdefgh]
  -->

<#--
如果使用了两个参数，那么第一个参数表示的含义和你使用一个参数时的相同，第二个
参数指定用什么东西来代替空白字符。比如：
-->
    [${""?left_pad(5, "-")}]
    [${"a"?left_pad(5, "-")}]
    [${"ab"?left_pad(5, "-")}]
    [${"abc"?left_pad(5, "-")}]
    [${"abcd"?left_pad(5, "-")}]
    [${"abcde"?left_pad(5, "-")}]

 <#--第二个参数也可以是个长度比 1 大的字符串。那么这个字符串会周期性的插入，
第二个参数必须是个字符串值，而且至少有一个字符
     [.oO.oO.o]
    [.oO.oO.a]
    [.oO.oOab]
    [.oO.oabc]
    [.oO.abcd]

 -->


<br>--right_pad 距右边<br>
    [${""?right_pad(5)}]
    [${"a"?right_pad(5)}]
    [${"ab"?right_pad(5)}]
    [${"abc"?right_pad(5)}]
    [${"abcd"?right_pad(5)}]
    [${"abcde"?right_pad(5)}]
    [${"abcdef"?right_pad(5)}]
    [${"abcdefg"?right_pad(5)}]
    [${"abcdefgh"?right_pad(5)}]<br>
    [${""?right_pad(8, ".oO")}]
    [${"a"?right_pad(8, ".oO")}]
    [${"ab"?right_pad(8, ".oO")}]
    [${"abc"?right_pad(8, ".oO")}]
    [${"abcd"?right_pad(8, ".oO")}]

<#--
[ ]
[a ]
[ab ]
[abc ]
[abcd ]
[abcde]
[abcdef]
[abcdefg]
[abcdefgh]
[.oO.oO.o]
[aoO.oO.o]
[abO.oO.o]
[abc.oO.o]
[abcdoO.o]
-->


<br>--contains 包含<br>

<#if "piceous"?contains("ice")>It contains "ice"</#if>
<#--
    It contains "ice"
-->

<br>--matches 匹配<br>
<#--
这是一个“超级用户”函数。不管你是否懂正则表达式。
注意：这个函数仅仅对使用 Java2 平台的 1.4 版本之后起作用。否则它会发生错误并中止模板的处理。
这个函数决定了字符串是否精确匹配上模式。而且，它返回匹配的子串列表。返回值是
一个多类型的值：
 布尔值：如果字符串精确匹配上模式返回 true，否则返回 false。例如，
"fooo"?matches('fo*') 是 true ， 但 是
"fooo bar"?matches('fo*')是 false。
 序列：字符串匹配子串的列表。可能是一个长度为 0 的序列。
 -->
<#if "fxo"?matches("f.?o")>Matches.<#else>Does not match.</#if>
<#assign res = "foo bar fyo"?matches("f.?o")>
    <br>
<#if res>Matches.<#else>Does not match.</#if>

<br> Matching_list sub-strings:<br>
<#list res as m>
    - ${m}<br>
</#list>


<#--
Matches.
Does not match.
Matching sub-strings:
- foo
- fyo
-->

  <br><#--如果正则表达式包含分组（括号），那么你可以使用内建函数 groups 来访问它们-->
<#assign res = "aa/rx; ab/r;"?matches("(\\w[^/]+)/([^;]+);")>
<#list res as m>
    - ${m} is ${m?groups[1]} per ${m?groups[2]}
</#list>
<#--
- aa/rx; is aa per rx
- ab/r; is ab per r
-->

<br>---number 数字格式:<br>
${2.6?string('0.00')}, <#--小数点后不足两位，用 0 代替 -->
${1239765.468 ?string('#.##')} ,<#--如果小数点后多余两位，就只保留两位，否则输出实际值 -->
${1239765.4?string('#.##')} ,

    <BR>
${1239765.468?string(',###.00')} - <#--整数部分每三位用,分割并且保证小数点后保留两位，不足用 0 代替 -->,
${1239765.4?string(',###.##')} -  <#--整数部分每三位用 , 分割，并且小数点后多余两位就只保留两位，不足两位就取实际位数，可以不不包含小数点 -->,
${1239765.4?string('###.00')}-

<BR>
<#setting number_format="#">

    <#--
freemarker在解析数据格式的时候，自动默认将数字按3为分割（1，000），这个问题给操作带来一定的额外处理复杂度，解决方法有如下几种：
1、在模板中直接加.toString()转化数字为字符串，如：
复制代码代码如下:
${languageList.id.toString()}；
2、在freemarker配置文件freemarker.properties加
复制代码代码如下:
<#setting number_format="#">或者 <#setting number_format="0">；
3、在模板中直接加<#setting number_format="#">或者<#setting number_format="0">，如：<#if
复制代码代码如下:
AdminLanguagePaginationMsg?exists>
<#setting number_format="#">
对于数字的格式化，可采用strng 和number_format 设置
数字格式化的优先级： string优先级最高，配置文件配置的优先级最低，在页面setting的优先级在两者之间。

    -->
    <br>replace 替换:<br>
${"this is a car acarus"?replace("car", "bulldozer")}<br>
    <#--替换是从左向右执行的-->
${"aaaaa"?replace("aaa", "X")}<br>
<#--如果第一个参数是空字符串 ，那么所有的空字符串将会被替换-->
${"foo"?replace("","|")}<br>

<#--
rtf 富文本
字符串作为富文本（ RTF 文本），也就是说，下列字符串：
 \替换为\\
 {替换为\{
 }替换为\}
-->

<br>--url URL 转义: URL 之后的字符串进行转义：<br>
<#--所有非 US-ASCII 的字符和保留的 URL 字符将会被%XX 形式来转义-->

<#assign xxx1 = 'a/b c'>
<#--${xxx1?url}-->
<#--a%2Fb%20c-->
<#--注意它会转义所有保留的 URL 字符（ /， =， &等），所以编码可以被用来对查询参数的
值进行，比如：
<a href="foo.cgi?x=${x?url}&y=${y?url}">Click here...</a>
-->
<#setting url_escaping_charset="UTF-8">

<#list "someMOOtestMOOtext"?split("MOO") as xx>
    ${xx_index}-${xx},
</#list>
<#--
- some
- test
- text
-->
<br>
<#list "some,,test,text,"?split(",") as x>
     "${x}",
</#list>
  <#--
   - "some"
    - ""
    - "test"
    - "text"
    - ""-->
<br>

<br>----starts_with 以…开头:<br>
   <#if  "redhead"?starts_with("red")>
       starts_with1
   </#if>
    <#if  "head"?starts_with("red")>
        starts_with2
    </#if>
 <br>---- string（当被用作是字符串值时）:<br>
<#--
仅仅返回和其内容一致的字符串。例外的是，如果值是一个多类型的值（比
如同时有字符串和序列两种），那么结果就只是一个简单的字符串，而不是多类型的值。 这
可以被用来防止多种人为输入
-->

<Br>--trim 去掉字符串首尾的空格<br>
(${" green mouse "?trim})

<br>--upper_case 大写形式<br>
(${" green mouse "?upper_case})

<br>--word_list 词列表:<br>
<#--包含字符串词的列表，并按它们在字符串中的顺序出现。词是连续的字符序列，包含任意字符，但是不包括空格。-->
<#assign words = " a bcd, . 1-2-3"?word_list>
<#list words as word>[${word}]</#list>

<br>--xhtml XHTML 格式:<br>
<#--
字符串作为 XHTML 格式文本输出，下面这些：
 <替换为&lt;
 >替换为&gt;
 &替换为&amp;
 "替换为&quot;
 '替换为&#39;
这个函数和 xml 函数的唯一不同是 xhtml 函数转义'为&#39;，而不是&apos;，
但是一些老版本的浏览器不正确解释&apos;。
-->

<br>--xml XML 格式:<br>
<#--
字符串作为 XML 格式文本输出，下面这些：
 <替换为&lt;
 >替换为&gt;
 &替换为&amp;
 "替换为&quot;
 '替换为&apos;
-->

<br>--通用标记:<br>
<#--
很多字符串函数接受一个可选的被称为“标记”的字符串参数。在这些字符串中，每一
个字符都影响着内建函数行为的一个特定方面。比如，字母 i 表示内建函数不应该在同一
个字母的大小写上有差异。标记中字母的顺序并不重要。
下面是标记字母的完整列表：
 i：大小写不敏感：不区分同一个字母大小写之间的差异。
 f：仅仅是第一。也就是说，替换/查找等，只是第一次出现的东西。
 r ：查 找的 子串 是正 则表 达式 。 FreeMarker 使用 变化 的正 则表 达式 ，在
http://java.sun.com/j2se/1.4.1/docs/api/java/util/regex/Pattern.html 中描述。 只有
你使用 Java2 平台的 1.4 版本以后，标记才会起作用。否则它会发生错误导致模板
处理停止。
 m：正则表达式多行模式。在多行模式下，表达式^和$仅仅匹配前后，分别是一
行结尾或者是字符串的结束。默认这些表达式仅仅匹配整个字符串的开头和结尾。
注意^和$不会匹配换行符本身。
 s： 启用正则表达式的 do-tall 模式（和 Perl 的单行模式一样）。在 do-tall 模式下，
表达式.匹配任意字符串，包括行结束符。默认这个表达式不匹配行结束符。
 c： 在正则表达式中许可空白和注释。
示例：
-->

<#assign s = 'foo bAr baar'>
${s?replace('ba', 'XY')}<br>
i: ${s?replace('ba', 'XY', 'i')}<br>
if: ${s?replace('ba', 'XY', 'if')}<br>
r: ${s?replace('ba*', 'XY', 'r')}<br>
ri: ${s?replace('ba*', 'XY', 'ri')}<br>
rif: ${s?replace('ba*', 'XY', 'rif')}<br>
<#--
foo bAr XYar
i: foo XYr XYar
if: foo XYr baar
r: foo XYAr XYr
ri: foo XYr XYr
rif: foo XYr baar
-->
<br>--处理数字的内建函数:<br>
<br>--c 数字转字符    :<br>

<br>--string（当用作是数值类型时） 数字转字符串:<br>
<#assign x=42>
${x},
${x?string} <#-- the same as ${x} -->,
${x?string.number},
${x?string.currency},
${x?string.percent},
${x?string.computer}
<br>
<#--
42
42
42
$42.00
4,200%
42

前三个表达式的输出是相同的，因为前两个表达式是默认格式，这里是数字。你可以使
用一个设置来改变默认设置：
-->
<#setting number_format="currency">
<#assign x=42>
${x}
${x?string} <#-- the same as ${x} -->
${x?string.number}
${x?string.currency}
${x?string.percent}
    <br>
<#--
$42.00
$42.00
42
$42.00
4,200%
-->

<#assign x = 1.234>
${x?string("0")} <br>
${x?string("0.#")}<br>
${x?string("0.##")}<br>
${x?string("0.###")}<br>
${x?string("0.####")}<br>
${1?string("000.00")}<br>
${12.1?string("000.00")}<br>
${123.456?string("000.00")}<br>
${1.2?string("0")}<br>
${1.8?string("0")}<br>
${1.5?string("0")} <-- 1.5, rounded towards even neighbor <br>
${2.5?string("0")} <-- 2.5, rounded towards even neighbor <br>
${12345?string("0.##E0")} <br>

<#--
在金融和统计学中，四舍五入都是根据所谓的一半原则，这就意味着对最近的“邻居”
进行四舍五入，除非离两个邻居距离相等，这种情况下，它四舍五入到偶数的邻居。如果你
注意看 1.5 和 2.5 的四舍五入的话，这在上面的示例中是可以看到的，两个都被四舍五入到
2，因为 2 是偶数，但 1 和 3 是奇数。
除 了 Java 小 数 语 法 模 式 之 外 ， 你 可 以 编 写 如
${aNumber?string("currency")} 这 样 的 代 码 ， 它 会 做 和
${aNumber?string.currency}一样的事情。
-->

<#--正如之前展示的预定义格式，默认的数字格式也可以在模板中进行设置-->
<#setting number_format="0.##">
${1.234}

<#setting locale="en_US">
    US people write: ${12345678?string(",##0.00")}
<#setting locale="hu">
    Hungarian people write: ${12345678?string(",##0.00")}

<#--
US people write: 12,345,678.00
Hungarian people write: 12 345 678,00
-->

<br>---round,floor,ceiling 数字的舍入处理:<br>
<#--
 内建函数 round 从 FreeMarker 2.3.13 版本之后才存在。
使用确定的舍入法则，转换一个数字到整数：
 round：返回最近的整数。如果数字以.5 结尾，那么它将进位（也就是说向正无
穷方向进位）
 floor： 返回数字的舍掉小数后的整数（也就是说向服务穷舍弃）
 ceiling：返回数字小数进位后的整数（也就是说向正无穷进位）
-->



<#assign testlist=[
0, 1, -1, 0.5, 1.5, -0.5,
-1.5, 0.25, -0.25, 1.75, -1.75]>
<#list testlist as result>
${result} ?floor=${result?floor} ?ceiling=${result?ceiling} ?round=${result?round} <br>
</#list>

<#--
0 ?floor=0 ?ceiling=0 ?round=0
1 ?floor=1 ?ceiling=1 ?round=1
-1 ?floor=-1 ?ceiling=-1 ?round=-1
0.5 ?floor=0 ?ceiling=1 ?round=1
1.5 ?floor=1 ?ceiling=2 ?round=2
-0.5 ?floor=-1 ?ceiling=0 ?round=0
-1.5 ?floor=-2 ?ceiling=-1 ?round=-1
0.25 ?floor=0 ?ceiling=1 ?round=0
-0.25 ?floor=-1 ?ceiling=0 ?round=0
1.75 ?floor=1 ?ceiling=2 ?round=2
-1.75 ?floor=-2 ?ceiling=-1 ?round=-2
-->
<br>处理日期的内建函数<br>

<br>string（当用作日期值时）日期转字符串<br>
<#--
 这个内建函数以指定的格式转换日期类型到字符串类型。（当默认格式由 FreeMarker 的
date_format， time_format 和 datetime_format 设置来支配时是很好的选择，那么你就不需要使用这个内建函数了。）
格式可以是预定义格式中的一种，或者你可以指定明确的格式化模式。
预定义的格式是 short， medium， long 和 full，它们定义了冗长的结果文本输
出。例如，如果输出的本地化是 U.S. English，而且时区是 U.S. Pacific，那么下面的代码：
-->

<#--<#setting time_zone="U.S. Pacific">
${openingTime?string.short}
${openingTime?string.medium}
${openingTime?string.long}
${openingTime?string.full}<br>
${nextDiscountDay?string.short}
${nextDiscountDay?string.medium}
${nextDiscountDay?string.long}
${nextDiscountDay?string.full}<br>
${lastUpdated?string.short}
${lastUpdated?string.medium}
${lastUpdated?string.long}
${lastUpdated?string.full}<br>-->

<br>--date， time， datetime （当使用日期值时）<br>
<#--
 date：仅仅年，月和日的部分被使用。
 ime：仅仅时，分，秒和毫秒的部分被使用。
 datetime：日期和时间量部分都使用。
-->
<#--
<#assign x = openingTime> &lt;#&ndash; no problem can occur here &ndash;&gt;
${openingTime?time} &lt;#&ndash; without ?time it would fail &ndash;&gt;
&lt;#&ndash; For the sake of better understanding, consider this: &ndash;&gt;
<#assign openingTime = openingTime?time>
${openingTime} &lt;#&ndash; this will work now &ndash;&gt;

Last updated: ${lastUpdated} &lt;#&ndash; assume that lastUpdated is adate-time value &ndash;&gt;
Last updated date: ${lastUpdated?date}
Last updated time: ${lastUpdated?time}
-->

<br>--seq_contanis 序列包含:<br>
<#assign x = ["red", 16, "blue", "cyan"]>
"blue": ${x?seq_contains("blue")?string("yes", "no")}<br>
"yellow": ${x?seq_contains("yellow")?string("yes", "no")}<br>
16: ${x?seq_contains(16)?string("yes", "no")}<br>
"16": ${x?seq_contains("16")?string("yes", "no")}<br>


<br>-- seq_index_of 第一次出现…时的位置:<br>
<#assign colors = ["red", "green", "blue"]>
${colors?seq_index_of("blue")}<br>
${colors?seq_index_of("red")}<br>
${colors?seq_index_of("purple")}<br>

<br>

    <#--
    搜索开始的地方可以由第二个可选的参数来确定。如果在同一个序列中相同的项可以多
次出现时，这是很有用的。第二个参数的数值没有什么限制：如果它是负数，那么就和它是
零的效果一样，而如果它是比序列长度还大的数，那么就和它是序列长度值的效果一样。小
数值会被切成整数。
    -->
<#assign names = ["Joe", "Fred", "Joe", "Susan"]>
    No 2nd param: ${names?seq_index_of("Joe")}<br>
    -2: ${names?seq_index_of("Joe", -2)}<br>
    -1: ${names?seq_index_of("Joe", -1)}<br>
    0: ${names?seq_index_of("Joe", 0)}<br>
    1: ${names?seq_index_of("Joe", 1)}<br>
    2: ${names?seq_index_of("Joe", 2)}<br>
    3: ${names?seq_index_of("Joe", 3)}<br>
    4: ${names?seq_index_of("Joe", 4)}<br>


<br> --seq_last_index_of 最后一次出现..的位置<br>
<#--
seq_ 前缀在这个内建函数中是需要的，用来和 last_index_of 区分开。
last_index_of 用于在字符串中搜索子串（因为一个变量可以同时是字符串和序列）。
返回序列中最后一次出现值的索引位置，，如果序列不包含指定的值时返回-1。也就是
说，和 seq_index_of 相同，只是在序列中从最后一项开始向前搜索。它也支持可选的
第二个参数来确定从哪里开始搜索的索引位置。比如：
-->
<#assign names = ["Joe", "Fred", "Joe", "Susan"]>
    No 2nd param: ${names?seq_last_index_of("Joe")}<br>
    -2: ${names?seq_last_index_of("Joe", -2)}<br>
    -1: ${names?seq_last_index_of("Joe", -1)}<br>
    0: ${names?seq_last_index_of("Joe", 0)}<br>
    1: ${names?seq_last_index_of("Joe", 1)}<br>
    2: ${names?seq_last_index_of("Joe", 2)}<br>
    3: ${names?seq_last_index_of("Joe", 3)}<br>
    4: ${names?seq_last_index_of("Joe", 4)}<br>

<br>--reverse 反转序列:<br>
 <#list names?reverse as n1>
     ${n1},
 </#list>

<br>--size 序列大小:<br>
${names?size}

<br>--sort 排序:<br>
<#assign ls = ["whale", "Barbara", "zeppelin", "aardvark","beetroot"]?sort>
<#list ls as i>${i}, </#list>
 <#foreach item in ls>
 ${item},
 </#foreach>

<#comment>和11111</#comment>


    <br>-- sort_by 以…来排序:<br>
<#assign ls = [
{"name":"whale", "weight":2000},
{"name":"Barbara", "weight":53},
{"name":"zeppelin", "weight":-200},
{"name":"aardvark", "weight":30},
{"name":"beetroot", "weight":0.3}
]>

    Order by name:
<#list ls?sort_by("name") as i>
    - ${i.name}: ${i.weight}
</#list>
    <br>
    Order by weight:
<#list ls?sort_by("weight") as i>
    - ${i.name}: ${i.weight}
</#list>
    <br>


<br>---chunk 区块<br>
<#--
这 个 内建 函数 分割 序列 到多 个大 小为 函数 的第 一个 参数 给定 的序 列（ 就 像
mySeq?chunk(3)）。结果是包含这些序列的一个序列。最后一个序列可能比给定的大
小要小，处分第二个参数也给定了（比如 mySeq?chunk(3, '-')），那个就是用来填
充最后一个序列，以达到给定的大小。
-->

<#assign seq = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']>
<#list seq?chunk(4) as row>
    <#list row as cell>${cell}, </#list>
</#list>
    <br>
<#list seq?chunk(4,'0') as row>
    <#list row as cell>${cell}, </#list>
</#list>

<br>---keys 键的集合:<br>
<#assign h = {"name":"mouse", "price":50}>
<#assign mkeys = h?keys>
<#list mkeys as mkey>${mkey} = ${h[mkey]}; </#list>

<br>---值的集合<br>
<#assign mvalues = h?values>
<#list mvalues as mvalue>${mvalue} </#list>
<br>

<br>----处理节点（ XML）的内建函数  <br>
<br>---children 子节点序列  <br>
<br>--parent 父节点  <br>
<br>---root 根节点<br>
<br>---ancestors 祖先节点  <br>
<br>---node_name 节点名称 <br>
<br>---node_type 节点类型  <br>
<br>---node_type 节点类型 <br>

    <br>--number_date， number_to_time， number_to_datetime:<br>
    <#--
    它们被用来转换数字（通常是 Java 的 long 类型）到日期，时间或时间日期类型。这
就使得它们和 Java 中的 new java.util.Date(long)是一致的。那也就是说，现在
数字可以被解释成毫秒数进行参数传递。数字可以是任意内容和任意类型，只要它的值可以
认为是 long 就行。如果数字不是完整的，那么它就会根据“五入”原则进行进位。这个
转换不是自动进行的。
     -->
${1305575275540?number_to_datetime}
${1305575275540?number_to_date}
${1305575275540?number_to_time}

    <br>---eval 求值<br>
${"1+2"?eval},
${"1+2"}   ,
${1+2}

<br>--- has_content 是否有内容<br>
<#--
 expr!?length > 0 来代替 expr?has_content。
-->

<br>---interpret 将字符串解释为 FTL 模板<br>
<#assign x=["a", "b", "c"]>
<#assign templateSource = r"<#list x as y>${y}</#list>"?interpret>
<#-- Note: That r was needed so that the ${y} is not interpretedabove -->
<@templateSource />


    <br>is_... 判断函数族<br>

    内建函数 如果值是…时返回 true
    is_string 字符串
    is_number 数字
    is_boolean 布尔值
    is_date 日期（所有类型：仅日期，仅时间和时间日期）
    is_method 方法
    is_transform 变换
    is_macro 宏
    is_hash 哈希表
    is_hash_ex 扩展的哈希表（也就是支持?keys 和?values）
    is_sequence 序列
    is_collection 集合
    is_enumerable 序列或集合
    is_indexable 序列
    is_directive 指令的类型（比如宏，或 TemplateDirectiveModel ，
    TemplateTransformModel 等）
    is_node 节点

<br>if， else， elseif 指令<br>

<br> 只有 if，没有 elseif 和 else：<br>
<#assign x=1>

<#if x == 1>
    x is 1
</#if>

<br>只有 if 和 else，没有 elseif：<br>
<#assign x=2>
<#if x == 1>
    x is 1
<#else>
    x is not 1
</#if>

<br>if 和两个 elseif，没有 else：<br>
<#assign x=3>

<#if x == 1>
    x is 1
<#elseif x == 2>
    x is 2
<#elseif x == 3>
    x is 3
</#if>


<br>if 和 3 个 elseif，还有 else：<br>
<#assign x=5>
<#if x == 1>
    x is 1
<#elseif x == 2>
    x is 2
<#elseif x == 3>
    x is 3
<#elseif x == 4>
    x is 4
<#else>
    x is not 1 nor 2 nor 3 nor 4
</#if>

<br>嵌套 if 指令：<br>
<#assign y=-5>
<#if x == 1>
    x is 1
    <#if y == 1>
        and y is 1 too
    <#else>
        but y is not
    </#if>
<#else>
    x is not 1
    <#if y < 0>
        and y is less than 0
    </#if>
</#if>

<#assign x=5>
 <#if (x > 1)>
 x>1
 </#if>
<br>
 <#if x &gt; 1>
     x>1
 </#if>

    <BR>
<BR>switch， case， default， break 指令<br>

<#assign xxx2=2>
<#switch xxx2>
    <#case 1>
        tis_1...
        <#break>
    <#case 2>
        ths_2...
        <#break>
    <#default>
        ths_default...
</#switch>

<#--
当它选择一个 case 指令后，它就会继续处
理 case 指令中的内容，直到遇到 break 指令。也就是它遇到另外一个 case 指令或
<#default>标记时也不会自动离开 switch 指令
-->
<#switch xxx2>
    <#case 1>
        1.
    <#case 2>
        2
    <#default>
        d
</#switch>

    <br>
<br>list， break 指令<br>
<#--

<#list sequence as item>
...
</#list>
这里：
 sequence：表达式将被算作序列或集合
 item：循环变量（不是表达式）的名称

你可以使用 list 指令来处理模板的一个部分中的一个序列中包含的各个变量。在开
标签和结束标签中的代码将会被处理，首先是第一个子变量，然后是第二个子变量，接着
第三个子变量，等等，直到超过最后一个。对于每个变量，这样的迭代中循环变量将会包
当前的子变量。
在 list 循环中，有两个特殊的循环变量可用：
 item_index：这是一个包含当前项在循环中的步进索引的数值。
 item_has_next：来辨别当前项是否是序列的最后一项的布尔值。
-->


<#assign seq = ["winter", "spring", "summer", "autumn"]>
<#list seq as x>
${x_index + 1}. ${x}<#if x_has_next>,</#if>
</#list>

<br>使用 list 在两个数字中来计数，使用一个数字范围序列表达式：<br>
<#assign x=3>
<#list 1..x as i>
${i}
</#list>

<#list seq as x>
${x}
    <#if x = "spring"><#break></#if>
</#list>


    <br>include 指令<br>
<#--
<#include path> or <#include path options>
这里：
 path：要包含文件的路径；一个算作是字符串的表达式。（用其他话说，它不用
是一个固定的字符串，它也可以是像 profile.baseDir + "/menu.ftl"
这样的东西。）
 options：一个或多个这样的选项：encoding=encoding, parse=parse
 encoding：算作是字符串的表达式
 parse：算作是布尔值的表达式（为了向下兼容，也接受一部分字符串值）

支持的 options 选项有：
 parse：如果它为真，那么被包含的文件将会当作 FTL 来解析，否则整个文件将被
视为简单文本（也就是说不会在其中查找 FreeMarker 的结构）。如果你忽略了这个
选项，那么它默认是 true。
 encoding：被包含文件从包含它的文件继承的编码方式（实际就是字符集），除非
你用这个选项来指定编码方式。编码名称要和 java.io.InputStreamReader 中支持的
那些一致（ 对于 Java API 1.3 版本： MIME 希望的字符集是从 IANA 字符集注册处得
到的）。合法的名字有： ISO-8859-2， UTF-8， Shift_JIS， Big5， EUC-KR， GB2312。
比如：

-->
    <br>include 指令:在你的模板中插入另外一个 FreeMarker 模板文件<br>
<#--
<#import path as hash>
这里：
 path:模板的路径。这是一个算作是字符串的表达式。（换句话说，它不是一个固
定的字符串，它可以是这样的一些东西，比如， profile.baseDir +
"/menu.ftl"。）
 hash：哈希表变量的结束名称，你可以由它来访问命名空间。这不是表达式。
-->


<#--import 指令不仅仅创建命名空间，而且要通过 import 的调用者（本例中的主命名空间）创建一个新的哈希表变量-->
<#--
 <br>import 指令:  命名空间<br>
<#import "./lib/my_test.ftl" as my>
<#assign mail="fred@acme.com">
&lt;#&ndash; 被称为"my"的哈希表就会是那个"大门" &ndash;&gt;
<@my.copyright date="1999-2002"/>
    1 my.mail=${my.mail}<br>
    2 mail=${mail}
    <br>
-->

    <br>noparse 指令:<br>
<#--
FreeMarker 不会在这个指令体中间寻找 FTL 标签，插值和其他特殊的字符序列，除了noparse 的结束标记。
-->

<#noparse>
    <#list animals as being>
        <tr><td>${being.name}<td>${being.price} Euros
    </#list>
</#noparse>

    <br>compress 指令:<br>
    <br>escape， noescape 指令:<br>
    (<#compress>
    1 2 3 4 5
    test only

    I said, test only

</#compress>)



<br> escape， noescape 指令:块中出现的插值（ ）会和转义表达式自动结合。<br>
<#--
<#escape x as x?html>
    First name: ${firstName}
    Last name: ${lastName}
    Maiden name: ${maidenName}
</#escape>
    First name: ${firstName?html}
    Last name: ${lastName?html}
    Maiden name: ${maidenName?html}
-->

<#--<#assign x3 = "<test>">
<#macro m1>
    m1: ${x3}
</#macro>

<#escape x3 as x3?html>
    <#macro m2>m2: ${x3}</#macro>
${x3}
<@m1/>
</#escape>

${x3}
<@m2/>-->

    <br>assign 指令:<br>

<#--
 <#assign name=value>
 or
 <#assign name1=value1 name2
 or
 <#assign same as above... i
 or
 <#assign name>
 capture this
 </#assign>
 or
 <#assign name in namespaceh
 capture this
 </#assign>
  name：变量的名字。不是表达式。而它可以本写作是字符串，如果变量名包含保
留字符这是很有用的，比如<#assign "foo-bar" = 1>。注意这个字符串
没有展开插值（如"${foo}"）。
 value：存储的值。是表达式。
 namespacehash：（通过 import）为命名空间创建的哈希表。是表达式。
用这个指令你可以创建一个新的变量，或者替换一个已经存在的变量。注意仅仅顶级变量可以被创建 /替换
 -->


<#assign seasons = ["winter", "spring", "summer", "autumn"]>
<#--<#assign test = test + 1>-->
<#--
使用一个 assign 标记来进行多次定义
<#assign
seasons = ["winter", "spring", "summer", "autumn"]
test = test + 1
>-->
<#--
命名空间（也就是和标签所在模板关联的命名空间）中创建变量。但如果你是用了 in
namespacehash，那么你可以用另外一个命名空间来创建/替换变量。比如，这里你在
命名空间中/mylib.ftl 创建/替换了变量 bgColor
-->
<#import "./lib/my_test.ftl" as my2>
<#assign bgColor="red" in my2>

<#--
assign 的极端使用是当它捕捉它的开始标记和结束标记中间生成的输出时。也就是
说，在标记之间打印的东西将不会在页面上显示，但是会存储在变量中。比如：
-->
<#macro myMacro>foo</#macro>
<#assign x>
    <#list 1..3 as n>
    ${n} <@myMacro />
    </#list>
</#assign>

Number of words: ${x?word_list?size}
${x}

<br>global 指令:<br>
<#--
概要
<#global name=value>
or
<#global name1=value1 name2=value2 ... nameN=valueN>
or
<#global name>
capture this
</#global>
这里：
 name：变量的名称。它不是表达式。但它可以被写作是字符串形式，如果变量名
包含保留字符这是很有用的，比如<#global "foo-bar" = 1>。注意这个
字符串没有扩展插值（如"${foo}"）。
 value：存储的值，是表达式。

这个指令和 assign 相似，但是被创建的变量在所有的命名空间中都可见，但又不会
存在于任何一个命名空间之中。精确地说，正如你会创建（或替换）一个数据模型变量。因
此，这个变量是全局的。如果在数据模型中，一个相同名称的变量存在的话，它会被使用这
个指令创建的变量隐藏。如果在当前的命名空间中，一个相同名称的变量存在的话，那么会
隐藏由 global 指令创建的变量。
比如<#global x = 1>，用创建了一个变量，那么在所有命名空间中 x 都可见，
除非另外一个称为 x 的变量隐藏了它（比如你已经用<#assign x = 2>创建了一个变
量）。 这种情形下，你可以使用特殊变量 globals，比如${.globals.x}。注意使用
globals 你看到所有全局可访问的变量；不但由 global 指令创建的变量，而且是数据
模型中的变量。
自定义 JSP 标记的用户请注意：用这个指令创建的变量集合和 JSP 页面范围对应。这就
意味着，如果自定义 JSP 标记想获得一个页面范围的属性（ page-scope bean），在当前命名
空间中一个相同名称的变量，从 JSP 标记的观点出发，将不会隐藏。
-->
<br>local 指令<br>
<#--
<#local name=value>
or
<#local name1=value1 name2=value2 ... nameN=valueN>
or
<#local name>
capture this
</#local>
这里：
 name：在 root 中局部对象的名称。它不是一个表达式。但它可以被写作是字符串
形式，如果变量名包含保留字符这是很有用的，比如<#global "foo-bar" =
1>。注意这个字符串没有扩展插值（如"${foo}"）。
 value：存储的值，是表达式。
它和 assign 指令类似，但是它创建或替换局部变量。这仅仅在宏和方法的内部定义
才会有作用。
要获得更多关于变量的信息，可以阅读：模板开发指南/其他/在模板中定义变量部分内
容。
-->


<br>setting 指令:<br>

<#--
<#setting name=value>
这里：
 name：设置的名称。不是表达式！
 value：设置的值，是表达式。

为进一步的处理而设置。设置是影响 FreeMarker 行为的值。新值仅仅在被设置的模板
处理时出现，而且不触碰模板本身。设置的初始值是由程序员设定的（参加：程序开发指南
/配置/设置信息部分的内容）。
支持的设置有：


 locale：输出的本地化（语言）。它可以影响数字，日期等显示格式。它的值是
由语言编码（小写两个字母的 ISO-639 编码）和可选的国家码（大写的两个字母
ISO-3166 编码）组成的字符串，它们以下划线相分隔，如果我们已经指定了国家那
么一个可选的不同编码（不是标准的）会以下划线分隔开国家。 合法的值示例：
en， en_US， en_US_MAC。 FreeMarker 会尝试使用最特定可用的本地化设置，
所以如果你指定了 en_US_MAC，但是它不被知道，那么它会尝试 en_US，然
后尝试 en，然后是计算机（可能是由程序员设置的）默认的本地化设置。
 number_format：当没有指定确定的格式化形式时，用来转化数字到字符串形
式的数字格式化设置。可以是下列中的一个预定义值 number（默认的），
computer， currency，或 percent。此外，以 Java 小数数字格式化语法
书写的任意的格式化形式也可以被指定。更多的格式形式请参考处理数字的内建函
数 string。
 boolean_format：以逗号分隔的一对字符串来分别展示 true 和 false 值，当
没有指定确定的格式时，转换布尔值到字符串。默认值是"true,false"。也
可以参考处理布尔值的内建函数 string。
 date_format， time_format， datetime_format：，当没有指定确定
的格式时，用来转换日期到字符串的日期/时间格式形式。如${someDate}.
date_format 这个情形，它只影响和日期相关的日期（年，月，日），
time_format 只 影 响 和 时 间 相 关 的 日 期 （ 时 ， 分 ， 秒 ， 毫 秒 ），
datetime_format 只影响时间日期类型的日期（ 年，月，日，时，分，秒，
毫秒）。这个设置的可能值和处理日期的内建函数 string 的参数相似；可以在
那部分参考更多内容。比如"short"，"long_medium"，"MM/dd/yyyy"。
 time_zone：时区的名称来显示并格式化时间。默认情况下，使用系统的时区。
也可以是 Java 时区 API 中的任何值。比如："GMT"， "GMT+2"， "GMT-1:30"，
"CET"， "PST"， "America/Los_Angeles"。
 url_escaping_charset：用来 URL 转义（比如${foo?url}）的字符集，
来计算转义（ %XX）的部分。通常包含 FreeMarker 的框架应该设置它，所以你不
应该在模板中来设置。（程序员可以阅读程序开发指南/其他/字符集问题部分来获
取更多内容）
 classic_compatible：这是对专业人员来说的，它的值应该是一个布尔值。
参见 freemarker.template.Configurable 的文档来获取更多信息。
示例：假设模板的初始本地化是 hu（ Hungarian，匈牙利），那么这个：
-->

${1.2}
<#setting locale="hu">
${1.2}
<#setting locale="en_US">
${1.2}

<br>用户自定义指令（ @...）<br>

<#--调用存储在变量 html_escape 中的指令-->
<@html_escape>
    a < b
    Romeo & Juliet
</@html_escape>



<Br>@结束标签<br>
<#--
你可以在结束标签中忽略 user_def_dir_exp。也就是说，你可以写</@>来替代
</@anything>。这个规则当表达式 user_def_dir_exp 太复杂时非常有用，因为
你不需要在结束标签中重复表达式。此外，如果表达式包含比简单变量名和点还多的表达式，
你 就 不 能 再 重 复 它 们 了 。 比 如
<@a_hash[a_method()]>...</@a_hash[a_method()]>就是错的，你必须
写 为 <@a_hash[a_method()]>...</@> 。 但 是
<@a_hash.foo>...</@a_hash.foo>是可以的。

一些自定义指令创建循环变量（和 list 指令相似）。 正如预定义指令（如 list）一
样，当你调用这个指令（如<#list foos as foo>...</#list>中的 foo）时循
环变量的名称就给定了，而变量的值是由指令本身设置的。在自定义指令的情形下，语法是
循环变量的名称在分号之后给定。比如：
-->

<br>@ 循环变量<br>
<#--
<@myRepeatMacro count=4 ; x7, last>
${x7}. Something... <#if last> This was the last!</#if>
</@myRepeatMacro>
-->


<Br>@位置参数传递<br>
<#--
位置参数传递（如<@heading "Preface", 1/>）是正常命名参数传递（如
<@heading title="Preface" level=1/>）的速记形式，这里忽略了参数的名
称。如果自定义指令只有一个参数，或者对于经常使用的自定义指令它参数的顺序很好记忆，
速记形式应该被应用。为了应用这种形式，你不得不了解声明的命名参数的顺序（如果指令
只有一个参数这是很琐碎的）。也就是，如果 heading 被创建为<#macro heading
title level>... ， 那 么 <@heading "Preface", 1/> 和 <@heading
title="Preface" level=1/> （ 或 <@heading level=1
title="Preface"/>；如果你使用参数名称，那顺序就不重要了）是相等的。要注意
位置参数传递现在仅仅支持宏定义。
-->


<br> macro， nested， return 指令<br>
没有参数的宏：<br>
<#macro mtest>
    Test text
</#macro>
<#-- call the macro: -->
<@mtest/>

<br>
有参数的宏：<br>
<#macro mtest2 foo bar baaz>
Test text, and the params: ${foo}, ${bar}, ${baaz}
</#macro>
<#-- call the macro: -->
<@mtest2 foo="a" bar="b" baaz=5*5-2/>


    <br>
有参数和默认值参数的宏：<br>

<#macro test foo bar="Bar" baaz=-1>
Test text, and the params: ${foo}, ${bar}, ${baaz}
</#macro>
<@test foo="a" bar="b" baaz=5*5-2/><br>
<@test foo="a" bar="b"/><br>
<@test foo="a" baaz=5*5-2/><br>
<@test foo="a"/><br>

    <br>
一个复杂的宏:<br>
<@list items=["mouse", "elephant", "python"] title="Animals"/>
    ...
<#macro list title items>
<p>${title?cap_first}:
<ul>
    <#list items as x>
    <li>${x?cap_first}
</#list>
</ul>
</#macro>

<br>
一个支持多个参数和命名参数的宏：<br>
<#macro img src extra...>
<img src="/context${src?html}"
    <#list extra?keys as attr>
    ${attr}="${extra[attr]?html}"
    </#list>
>
</#macro>
<@img src="/images/test.png" width=100 height=50 alt="Test"/>

<br>nested 指令:<br>
<#--
nested 指令执行自定义指令开始和结束标签中间的模板片段。嵌套的片段可以包含
模板中合法的任意内容：插值，指令…等。它在上下文环境中被执行，也就是宏被调用的地
方，而不是宏定义体的上下文中。因此，比如，你不能看到嵌套部分的宏的局部变量。如果
你没有调用 nested 指令，自定义指令开始和结束标记中的部分将会被忽略。
-->

<#macro do_twice>
1. <#nested>
2. <#nested>
</#macro>
<@do_twice>something<br></@do_twice>

<br>嵌套指令可以对嵌套内容创建循环变量:<br>
<#macro do_thrice>
    <#nested 1>
    <#nested 2>
    <#nested 3>
</#macro>

<@do_thrice ; x>
${x} Anything.<br>
</@do_thrice>

<br>

<#macro repeat count>
    <#list 1..count as x>
        <#nested x, x/2, x==count>
    </#list>
</#macro>
<@repeat count=4 ; c, halfc, last>
${c}. ${halfc}<#if last> Last!</#if><br>
</@repeat>

<br>return指令：<br>
<#macro test>
Test text
    <#return>
Will not be printed.
</#macro>
<@test/>



<br>function，return 指令<br>
<#function avg x y>
    <#return (x + y) / 2>
</#function>
${avg(10, 20)}

<br>创建一个方法来计算多个数字的平均值:<br>

<#function avg nums...>
    <#local sum = 0>
    <#list nums as num>
        <#local sum = sum + num>
    </#list>
    <#if nums?size != 0>
        <#return sum / nums?size>
    </#if>
</#function>
${avg(10, 20)}<br>
${avg(10, 20, 30, 40)}<br>
${avg()!"N/A"}<br>

<br>attempt， recover 指令:<br>
Primary content,
<#attempt>
    Optional content: ${thisMayFails}-
    <#recover>
    Ops! The optional content is not available.-
</#attempt>
Primary content continued


<br>FTL 中的保留名称:<br>

<#--
下面的这些名称不能在非方括号语法中被用作顶层变量（比如 vars["in"]），因为
这是 FTL 中的关键字。
 true：布尔值“ true”
 false：布尔值“ false”
 gt：比较运算符“大于”
 gte：比较运算符“大于或等于”
 lt：比较运算符“小于”
 lte：比较运算符“小于或等于”
 as：由少数指令使用  list ls as l
 in：由少数指令使用  foreach l in ls
 using：由少数指令使用
-->

<#if name?? >
abc1
</#if>

<br>
<#if name?exists>
abc2
</#if>



 <#include "footer.ftl" >
  </body>
 </html>